package com.comven.example.utils;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;

import java.io.IOException;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;


public class DateJsonSerializer {

    private static final Integer HOURS = 8;

    private static ZoneOffset zoneOffset = ZoneOffset.ofHours(HOURS);

    public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

        @Override
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(String.valueOf(localDateTime.toInstant(zoneOffset).toEpochMilli()));
        }

        @Override
        public void serializeWithType(LocalDateTime localDateTime, JsonGenerator g, SerializerProvider provider,
                                      TypeSerializer typeSer) throws IOException {
            WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(localDateTime, JsonToken.VALUE_STRING));
            serialize(localDateTime, g, provider);
            typeSer.writeTypeSuffix(g, typeIdDef);
        }
    }

    public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {

        @Override
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            return LocalDateTime.ofInstant(Instant.ofEpochMilli(jsonParser.getLongValue()), zoneOffset);
        }

        @Override
        public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
            return typeDeserializer.deserializeTypedFromAny(p, ctxt);
        }

    }

    public static class LocalDateSerializer extends JsonSerializer<LocalDate> {

        @Override
        public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(String.valueOf(localDate.atStartOfDay(zoneOffset).toInstant().toEpochMilli()));
        }

        @Override
        public void serializeWithType(LocalDate localDate, JsonGenerator g, SerializerProvider provider,
                                      TypeSerializer typeSer) throws IOException {
            WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(localDate, JsonToken.VALUE_STRING));
            serialize(localDate, g, provider);
            typeSer.writeTypeSuffix(g, typeIdDef);
        }
    }

    public static class LocalDateDeserializer extends JsonDeserializer<LocalDate> {

        @Override
        public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            return LocalDateTime.ofInstant(Instant.ofEpochMilli(jsonParser.getLongValue()), zoneOffset).toLocalDate();
        }

        @Override
        public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
            return typeDeserializer.deserializeTypedFromAny(p, ctxt);
        }

    }

    public static class DateSerializer extends JsonSerializer<Date> {

        @Override
        public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(String.valueOf(date.getTime()));
        }

        @Override
        public void serializeWithType(Date value, JsonGenerator g, SerializerProvider provider,
                                      TypeSerializer typeSer) throws IOException {
            WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(value, JsonToken.VALUE_STRING));
            serialize(value, g, provider);
            typeSer.writeTypeSuffix(g, typeIdDef);
        }
    }

    public static class DateDeserializer extends JsonDeserializer<Date> {

        @Override
        public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            return new Date(Long.valueOf(jsonParser.getValueAsString()));
        }

        @Override
        public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
            return typeDeserializer.deserializeTypedFromAny(p, ctxt);
        }

    }

    public static class TimestampSerializer extends JsonSerializer<Timestamp> {

        @Override
        public void serialize(Timestamp timestamp, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(String.valueOf(timestamp.getTime()));
        }

        @Override
        public void serializeWithType(Timestamp timestamp, JsonGenerator g, SerializerProvider provider,
                                      TypeSerializer typeSer) throws IOException {
            WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(timestamp, JsonToken.VALUE_STRING));
            serialize(timestamp, g, provider);
            typeSer.writeTypeSuffix(g, typeIdDef);
        }
    }

    public static class TimestampDeserializer extends JsonDeserializer<Timestamp> {

        @Override
        public Timestamp deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            return new Timestamp(jsonParser.getLongValue());
        }

        @Override
        public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
            return typeDeserializer.deserializeTypedFromAny(p, ctxt);
        }

    }

}
